package com.tongan.generate.tinyextend;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.thoughtworks.xstream.XStream;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.lang.StringUtils;
import org.tinygroup.codegen.CodeGenerator;
import org.tinygroup.codegen.config.MacroDefine;
import org.tinygroup.commons.tools.StringUtil;
import org.tinygroup.context.Context;
import org.tinygroup.context.util.ContextFactory;
import org.tinygroup.docgen.DocumentGenerater;
import org.tinygroup.docgen.config.GenUtilConfig;
import org.tinygroup.docgen.config.StaticClass;
import org.tinygroup.docgen.function.StaticClassFunction;
import org.tinygroup.logger.LogLevel;
import org.tinygroup.logger.Logger;
import org.tinygroup.logger.LoggerFactory;
import org.tinygroup.template.TemplateEngine;
import org.tinygroup.vfs.FileObject;
import org.tinygroup.vfs.VFS;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Created by kenti on 28/05/2017.
 */
public class MyCodeGenerator  implements CodeGenerator{

    private static final Logger LOGGER = LoggerFactory.getLogger(MyCodeGenerator.class);

    @Getter
    //public static FullContextFileRepository repository;//暂时定为静态
    private DocumentGenerater<TemplateEngine> generater;

    public DocumentGenerater<TemplateEngine> getGenerater() {
        return generater;
    }

    public void setGenerater(DocumentGenerater<TemplateEngine> generater) {
        this.generater = generater;
    }


    /**
     * @param context 默认文件需要用到的上下文环境
     * @throws IOException
     */
    public List<String> generate(Context context) throws IOException {
        NewMetadata metaData = context.get(CODE_META_DATA);
        if (metaData == null) {
            throw new RuntimeException("代码生成器的元数据不存在");
        }
        addUtilClass();
        List<MacroDefine> macroDefines = metaData.getMacroDefines();
        for (MacroDefine macroDefine : macroDefines) {
            LOGGER.logMessage(LogLevel.INFO, "开始加载宏文件路径：{0}", macroDefine.getMacroPath());
            String macroPath = macroDefine.getMacroPath();
            if (!StringUtils.startsWith(macroPath, "/")) {
                macroPath = "/" + macroPath;
            }
            FileObject fileObject = VFS.resolveFile((String) context.get(ABSOLUTE_PATH));
            generater.addMacroFile(fileObject.getFileObject(macroPath));
            LOGGER.logMessage(LogLevel.INFO, "宏文件路径：{0}，加载完毕", macroDefine.getMacroPath());
        }
        List<com.tongan.generate.tinyextend.TemplateDefine> templateDefines = metaData.getTemplateDefines();
        List<String> fileList = new ArrayList<String>();
        for (TemplateDefine templateDefine : templateDefines) {
            Context newContext = createNewContext(context, templateDefine);
            //将tiny环境的所有变量输出到模版页面
            Map m = new HashMap();
            m.putAll(newContext.getTotalItemMap());
            m.putAll(context.getTotalItemMap());
            newContext.put("json", toJson(m));
            String templatePath = templateDefine.getTemplatePath();
            LOGGER.logMessage(LogLevel.INFO, "开始加载模板文件路径：{0}", templatePath);
            FileObject templateDirObject = VFS.resolveFile((String) context.get(ABSOLUTE_PATH));
            LOGGER.logMessage(LogLevel.INFO, "模板文件路径：{0}，加载完毕", templatePath);
            FileObject templateFileObject = templateDirObject.getFileObject(templatePath);
            generater.addMacroFile(templateFileObject);
            if (!templateDefine.getIterateOutput().booleanValue()) {
                if (templateDefine.getOutput().booleanValue()) {
                    try {
                        File file = outputFile(newContext, templateFileObject, templateDefine);
                        fileList.add(file.getPath());
                    }catch (Exception e){
                        LOGGER.logMessage(LogLevel.ERROR, "模板文件路径：{0}，出错", e);
                    }
                }
            } else if (templateDefine.getOutput().booleanValue()) {
                //执行循环输出模版
                if (interateOutput != null) {
                    interateOutput.prepare(this, newContext, templateDefine, templateFileObject);
                }
            }
        }
        return fileList;
    }

    static SerializerFeature[] serializerFeature={
            SerializerFeature.WriteDateUseDateFormat,
            SerializerFeature.QuoteFieldNames,
            SerializerFeature.WriteNullStringAsEmpty,
            SerializerFeature.WriteMapNullValue,
            SerializerFeature.WriteNullBooleanAsFalse,
            SerializerFeature.WriteNullNumberAsZero,
            SerializerFeature.WriteNullListAsEmpty,
            SerializerFeature.PrettyFormat
    } ;

    public static String toJson(Object object){
        String json = JSON.toJSONString(object,serializerFeature);
        return json;
    }


    public File outputFile(Context newContext, FileObject templateFileObject,TemplateDefine templateDefine) throws Exception {
        String generateFile = generater.evaluteString(newContext, templateDefine.getFileNameTemplate());
        File file = new File(generateFile);
        if (!file.exists()) {
            file.getParentFile().mkdirs();
            file.createNewFile();
        }
        OutputStream outputStream = new FileOutputStream(file);
        try {
            generater.generate(templateFileObject, newContext, new FileOutputStream(file));
            LOGGER.logMessage(LogLevel.INFO, "模板输出文件路径：{0}，生成完毕", generateFile);
        }finally {
            outputStream.close();
        }
        return file;
    }

    @Setter
    @Getter
    InterateOutput interateOutput;

    /**
     * 当有些模版需要被循环输出时进行的回调接口
     */
    public static interface InterateOutput{
        public void prepare(CodeGenerator thisGener,Context currentContext,TemplateDefine templateDefine,FileObject templateFileObject);
    }

    private void addUtilClass() {
        XStream stream = new XStream();
        stream.setClassLoader(getClass().getClassLoader());
        stream.autodetectAnnotations(true);
        stream.processAnnotations(GenUtilConfig.class);
        GenUtilConfig config = (GenUtilConfig) stream.fromXML(getClass().getResourceAsStream("/codegen.util.xml"));
        if (config.getStaticClasses() != null) {
            for (StaticClass staticClass : config.getStaticClasses()) {
                try {
                    StaticClassFunction function = new StaticClassFunction(staticClass.getName(), staticClass.getClassName());
                    generater.getTemplateGenerater().addTemplateFunction(function);
                } catch (Exception e) {
                    LOGGER.logMessage(LogLevel.ERROR, "静态方法类：{0}，实例化失败", staticClass.getClassName());
                }
            }
        }
    }

    private Context createNewContext(Context context, TemplateDefine templateDefine) {
        Context newContext = ContextFactory.getContext();
        newContext.setParent(context);
        newContext.put(TEMPLATE_FILE, templateDefine);
        String templatePath = templateDefine.getTemplatePath();
        String templateFilePath = templatePath.replaceAll("/", "\\" + File.separator);//把/../..路径转化成系统认知的路径格式
        String path = StringUtil.substringBeforeLast(templateFilePath, File.separator);
        if (path.startsWith(File.separator)) {
            path = path.substring(1);
        }
        if (path.trim().length() > 0 && !path.endsWith(File.separator)) {
            path = path + File.separator;
        }
        newContext.put("templateFilePath", path);
        String fileName = StringUtil.substringAfterLast(templateFilePath, File.separator);
        newContext.put("templateFileName", StringUtil.substringBeforeLast(fileName, "."));
        return newContext;
    }
}
